# 克隆自聚宽文章：https://www.joinquant.com/post/9612
# 标题：【新手入门教程】市值轮动策略3.0
# 作者：JoinQuant-TWist

# 克隆自聚宽文章：https://www.joinquant.com/post/9612
# 标题：【新手入门教程】市值轮动策略3.0
# 作者：JoinQuant-TWist

# 代码性能分析
enable_profile()
# 导入技术指标库
from jqlib.technical_analysis import *

# 初始化
def initialize(context):
    g.stocksnum = 5 # 持有最小市值股票数
    g.period = 10 # 轮动频率
    g.days = 1 # 记录策略进行到第几天，初始为1
    
    # 过滤掉order系列API产生的比error级别低的log
    log.set_level('order', 'error')
    # 开盘前运行
    run_daily(daily, time='before_open')
    # 每个设置的Bar（在这里是分钟）运行
    run_daily(mktopen, time='every_bar')
    g.holdpct =1 # 持仓水平的百分比
    
def daily(context):
    # 判断策略进行天数是否能被轮动频率整除余1
    if g.days % g.period == 1:
        # 选出要交易的股票
        buylist=pick(context)
        # 根据大盘情况决定持仓水平
        g.holdpct=mkt_index(context)
        # 交易下单
        trade(context,buylist)
    else:
        pass # 什么也不做
    g.days = g.days + 1 # 策略经过天数增加1

def mktopen(context):
    # 每日每分钟止损
    stop(context)
    
# 选股
def pick(context):
    # 获取当前时间
    date=context.current_dt.strftime("%Y-%m-%d")
    # 获取板块、概念的股票代码并连接起来
    scu = get_industry_stocks('HY007')+get_industry_stocks('801180')+get_concept_stocks('GN069')
    # print "\nscu共%d个股票" % (len(scu))

    # 选出在scu内的股票的股票代码，并按照当前时间市值从小到大排序
    df = get_fundamentals(query(
            valuation.code,valuation.market_cap
        ).filter(
            valuation.code.in_(scu)
        ).order_by(
            valuation.market_cap.asc()
        ), date=date
        )

    # 取出前g.stocksnum名的股票代码，并转成list类型，buylist为选中的股票
    buylist =list(df['code'][:g.stocksnum])
    # 过滤停牌股票
    buylist=paused_filter(buylist)
    # 过滤涨停股票
    buylist=high_limit_filter(context,buylist)
    # 过滤st股票
    buylist=st_filter(buylist)
    return buylist
# 交易
def trade(context,buylist):
    # 对于每个当下持有的股票进行判断：现在是否已经不在buylist里，如果是则卖出
    for stock in context.portfolio.positions:
        if stock not in buylist: #如果stock不在buylist
            order_target(stock, 0) #调整stock的持仓为0，即卖出

    # 将总资产(现金+股票)除以持股数g.stocksnum
    position_per_stk = context.portfolio.total_value*g.holdpct/g.stocksnum
    # 调整buylist中的每个股票持仓价值为position_per_stk
    for stock in buylist:
        order_target_value(stock, position_per_stk)
# 止损
def stop(context):
    # 循环查看持仓的每个股票
    for stock in context.portfolio.positions:
        # 如果股票最新价格除以平均成本小于0.8，即亏损超过20%
        if context.portfolio.positions[stock].price/context.portfolio.positions[stock].avg_cost < 0.8: 
            # 调整stock的持仓为0，即卖出
            order_target(stock, 0) 
            # 输出日志：股票名 止损
            print "\n%s 止损" % stock

# 大盘情况
def mkt_index(context):
    
    mktindex=history(count=1,field='close',security_list=['000300.XSHG'])
    mktindex=mktindex.ix[-1,'000300.XSHG']
    
    # 获取当前时间
    date=context.current_dt.strftime("%Y-%m-%d")
    ma20=MA(['000300.XSHG'], check_date=date, timeperiod=20)
    ma20=ma20['000300.XSHG']
    # print "\n大盘：%f\n大盘20均线：%f" % (mktindex,ma)
    if mktindex>ma20:
        return 1
    else:
        return 0.5
    
# 过滤停牌股票
# https://www.joinquant.com/algorithm/apishare/get?apiId=20
def paused_filter(security_list):
    current_data = get_current_data()
    security_list = [stock for stock in security_list if not current_data[stock].paused]
    # 返回结果
    return security_list

# 过滤涨停股票
# https://www.joinquant.com/algorithm/apishare/get?apiId=24
def high_limit_filter(context, security_list):
    current_data = get_current_data()
    security_list = [stock for stock in security_list if not (current_data[stock].day_open>current_data[stock].high_limit*0.985)]
    # 返回结果
    return security_list

# 过滤ST股票
# https://www.joinquant.com/algorithm/apishare/get?apiId=22
def st_filter(security_list):
    current_data = get_current_data()
    security_list = [stock for stock in security_list if not current_data[stock].is_st]
    # 返回结果
    return security_list           
